#include "mpx.h"
#include "commands.h"
#include "support.h"
#include "mpx_supt.h"
#include "pcb_strc.h"
#include "com_dict.h"
#include "com_hand.h"
#include "err_hand.h"
#include "dispatch.h"
#include "com_help.h"

static date_rec gbl_date;

/* ############################# MODULE R1 ############################# */

int cmd_example(char *args) {
	printf("Example command, argument received:%s\n",args);
	return 0;
}

int cmd_echo(char *args) {
	printf("%s\n",args);
	return 0;
}

int cmd_help(char *args) {
	command_rec *command;

	if (strlen(args) == 0) {
		printf("Command Listing:\n");
		list_commands();
		printf("\nDetailed help usage: help cmdname\n");
	}
	else {
		command = get_command(args);
		if (command == NULL) {
			printf("No such command: %s\n",args);
		}
		else {
			printf("Help for the '%s' command:\n%s\n%s\n",command->name,command->desc,command->help);
		}
	}
	return 0;
}

int cmd_dir(char *args) {
	char name_buf[MAX_FILE_NAME_LENGTH];
	int buff_size;
	long file_size;
	int reading, code;
	char lines[(TERMINAL_COLS*80)*sizeof(char)];
	char tmp[256*sizeof(char)];
	
	buff_size=MAX_FILE_NAME_LENGTH;
	reading = 1;
	
	printf("%-*s %s\n",MAX_FILE_NAME_LENGTH,"File Name","Size (bytes)");
	printf("--------------------------------\n");
	
	strcpy(lines,"");
	
	sys_open_dir(".");
	while (reading) {
		code = sys_get_entry(name_buf,buff_size,&file_size);
		if (code != 0)
			reading = 0;
		else {
			sprintf(tmp,"%-*s %d\n",MAX_FILE_NAME_LENGTH,name_buf,file_size);
			strcat(lines,tmp);
		}
	}
	sys_close_dir();
	printf("%s",lines);
	return OK;
}

int cmd_date(char *args) {
	char new_mon[3], new_day[3], new_year[5];
	int i_mon, i_day, i_year;
	date_rec new_date;
	int date_valid;
	
	date_valid = 1;
	if (strlen(args) == 0) {
		sys_get_date(&gbl_date);
		printf("Today's date is: %d/%d/%d\n",gbl_date.month,gbl_date.day,gbl_date.year);
	}
	else if (strlen(args) == 10) {//mm-dd-yyyy
		new_mon[0] = args[0];
		new_mon[1] = args[1];
		new_mon[2] = '\0';
		i_mon = atoi(new_mon);
		
		new_day[0] = args[3];
		new_day[1] = args[4];
		new_day[2] = '\0';
		i_day = atoi(new_day);
		
		
		new_year[0] = args[6];
		new_year[1] = args[7];
		new_year[2] = args[8];
		new_year[3] = args[9];
		new_year[4] = '\0';
		i_year = atoi(new_year);
		
		//Valid date checking
		if (i_mon < 1 || i_mon > 12) {
			date_valid = 0;
			printf("Error: invalid month\n");
		}
		if (i_year == 0) {
			printf("Error: invalid year\n");
		}
		if (i_day < 1 || i_day > 31) {
			date_valid = 0;
			printf("Error: days not sensible (out of range)\n");
		}
		if ( (i_mon	== 9 || i_mon == 4 || i_mon == 6 || i_mon == 11) && i_day == 31 ) { //30 days hath...
			date_valid = 0;
			printf("Error: there aren't 31 days in that month\n");
		}
		if ( i_mon == 2 && i_day > 29 ) {
			date_valid = 0;
			printf("Error: there aren't that many days in February, ever\n");
		}
		if ( i_mon == 2 && i_day == 29 && !(i_year % 400 == 0 || (i_year %100 != 0 && i_year%4 == 0) )) {
			date_valid = 0;
			printf("Error: %d isn't a leap year.\n",i_year);
		}
		
		if (date_valid) {
			new_date.month = atoi(new_mon);
			new_date.day = atoi(new_day);
			new_date.year = atoi(new_year);
			sys_set_date(&new_date);
			sys_get_date(&gbl_date);
			printf("System Date Changed to %d-%d-%d\n",gbl_date.month,gbl_date.day,gbl_date.year);
		}
		else {
			printf("Date not changed\n");
		}
	}
	else {
		printf("Usage: date mm dd yyyy\n");
	}
	return OK;
}

int cmd_version(char *args) {
	printf(MPX_VERSION_LONG);
	return 0;
}

int cmd_quit(char *args) {
	char *input;
	int buff_len, return_code;
	
	buff_len = COM_USER_INPUT_LENGTH;
	
	printf("Are you sure? Type yes to quit\n");
	
	printf("%s",gbl_prompt);
	gets(input);
	
	if( strcmp(input,"yes") == 0 || strcmp(input,"y") == 0) {
		return_code = QUIT;
	}
	else {
		printf("Well okay then: Aborting Quit\n");
		return_code = 0;
	}
	
	return return_code;
}

/* ############################# MODULE R2 ############################# */

int cmd_create_pcb(char *args) {
	char *name, *str_class, *str_pri;
	int pcb_class, priority;
	pcb_rec *new_pcb = NULL;
		
	name = strtok(args," ,\t\n");
	str_class = strtok(NULL," ,\t\n");
	str_pri = strtok(NULL," ,\t\n");
	
	//User didn't supply enough arguments
	if (name == NULL || str_class == NULL || str_pri == NULL) {
		printf(HELP_CREATE);
		return ERROR;
	}
	
	pcb_class = atoi(str_class);
	priority = atoi(str_pri);
	
	//atoi sucks, will convert to 0 if input is bogus
	if (priority == 0 && strcmp(str_pri,"0") != 0) { 
		printf("Error: Priority must be an integer between -128 and 127\n");
		return ERROR;
	}
	
	new_pcb = setup_pcb(name, priority, pcb_class);
		
	if (new_pcb != NULL) {
		insert_pcb(new_pcb);
		printf("Successfully created process %s\n",name);
		return OK;
	}
	else {
		printf("Failed to create process %s\n",name);
		return ERROR;
	}
}

int cmd_delete_pcb(char *args) {
	pcb_rec *pcb;
	char *name;
	
	name = args;
	pcb = find_pcb(name);
	
	if (pcb != NULL) {
		remove_pcb(pcb);
		free_pcb(pcb);
		printf("%s deleted\n",name);
		return OK;
	}
	else {
		printf("ERROR: could not locate PCB named \"%s\"\n",name);
		return ERROR;
	}
}

int cmd_block(char *args) {
	pcb_rec *pcb;
	char *name;
	
	name = args;
	pcb = find_pcb(name);
	
	if (pcb == NULL) {
		printf("ERROR: could not locate PCB named \"%s\"\n",name);
		return ERROR;
	}
	if (pcb->proc_class == PROC_CLASS_SYS) {
		printf("ERROR: cannot block a system process\n");
		return ERROR;
	}
	switch(pcb->state) {
		case PROC_STATE_READY:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_BLOCKED;
			insert_pcb(pcb);
			printf("Ready process %s now blocked\n",pcb->name);
			break;
		case PROC_STATE_BLOCKED:
			printf("Error: Can't block %s. It is already blocked.\n",pcb->name);
			return ERROR;
		case PROC_STATE_SUSPENDED_READY:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_SUSPENDED_BLOCKED;
			insert_pcb(pcb);
			printf("Suspended-ready process %s now suspended-blocked\n",pcb->name);
			break;
		case PROC_STATE_SUSPENDED_BLOCKED:
			printf("Error: Can't block %s. It is already blocked.\n",pcb->name);
			return ERROR;
		default:
			printf("ERROR: Invalid process state for %s\n",pcb->name);
	}
	
	return OK;
}

int cmd_unblock(char *args) {
	pcb_rec *pcb;
	char *name;
	
	name = args;
	pcb = find_pcb(name);
	
	if (pcb == NULL) {
		printf("ERROR: could not locate PCB named \"%s\"\n",name);
		return ERROR;
	}
	switch(pcb->state) {
		case PROC_STATE_READY:
			printf("Error: Can't unblock %s. It isn't blocked.\n",pcb->name);
			return ERROR;
		case PROC_STATE_BLOCKED:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_READY;
			insert_pcb(pcb);
//			printf("Blocked process %s now ready\n",pcb->name);
			break;
		case PROC_STATE_SUSPENDED_READY:
			printf("Error: Can't unblock %s. It isn't blocked.\n",pcb->name);
			return ERROR;
		case PROC_STATE_SUSPENDED_BLOCKED:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_SUSPENDED_READY;
			insert_pcb(pcb);
			printf("Suspended-blocked process %s now suspended-ready\n",pcb->name);
			break;
		default:
			printf("ERROR: Invalid process state for %s\n",pcb->name);
	}
	
	return OK;
}
int cmd_suspend(char *args) {
	pcb_rec *pcb;
	char *name;
	int t;
	
	name = args;
	pcb = find_pcb(name);
	
	if (pcb == NULL) {
		printf("ERROR: could not locate PCB named \"%s\"\n",name);
		return ERROR;
	}
	if (pcb->proc_class == PROC_CLASS_SYS) {
		printf("ERROR: cannot suspend a system process\n");
		return ERROR;
	}

	switch(pcb->state) {
		case PROC_STATE_READY:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_SUSPENDED_READY;
			insert_pcb(pcb);
			printf("Ready process %s now suspended\n",pcb->name);
			break;
		case PROC_STATE_BLOCKED:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_SUSPENDED_BLOCKED;
			insert_pcb(pcb);
			printf("Blocked process %s now suspended-blocked\n",pcb->name);
			break;
		case PROC_STATE_SUSPENDED_READY:
			printf("Error: Can't suspend %s. It is already suspended.\n",pcb->name);
			return ERROR;
		case PROC_STATE_SUSPENDED_BLOCKED:
			printf("Error: Can't suspend %s. It is already suspended.\n",pcb->name);
			return ERROR;
		default:
			printf("ERROR: Invalid process state for %s\n",pcb->name);
	}
	
	return OK;
}
int cmd_resume(char *args) {
	pcb_rec *pcb;
	char *name;
	
	name = args;
	pcb = find_pcb(name);
	
	if (pcb == NULL) {
		printf("ERROR: could not locate PCB named \"%s\"\n",name);
		return ERROR;
	}
	switch(pcb->state) {
		case PROC_STATE_READY:
			printf("Error: Can't resume %s. It isn't suspended.\n",pcb->name);
			return ERROR;
		case PROC_STATE_BLOCKED:
			printf("Error: Can't resume %s. It isn't suspended.\n",pcb->name);
			return ERROR;
		case PROC_STATE_SUSPENDED_READY:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_READY;
			insert_pcb(pcb);
			printf("Suspended-ready process %s now ready\n",pcb->name);
			break;
		case PROC_STATE_SUSPENDED_BLOCKED:
			remove_pcb(pcb);
			pcb->state = PROC_STATE_BLOCKED;
			insert_pcb(pcb);
			printf("Suspended-blocked process %s now blocked\n",pcb->name);
			break;
		default:
			printf("ERROR: Invalid process state for %s\n",pcb->name);
	}
	
	return OK;
}
int cmd_priority(char *args) {
//	printf("Changes the priority of a process\n");
//	printf("Usage: priority name priority\n");
	pcb_rec *pcb;
	char *name;
	int priority;
	
	name = strtok(args," ");
	if (name == NULL) {
		mpx_error(ERR_PROC_NAME_INVALID,"cmd_priority: name is null");
		return ERROR;
	}
	
	priority = atoi(strtok(NULL," "));
	if ( priority > 127 || priority < -128 || priority == 0 ) {
		mpx_error(ERR_PROC_INVALID_PRIORITY,"cmd_priority: bad priority value");
		return ERROR;
	}
	
	pcb = find_pcb(name);
	if (pcb != NULL) {
		pcb->priority = priority;
		if (find_queue(pcb)->queue_type == QUEUE_TYPE_PRIORITY) {
			remove_pcb(pcb);
			insert_pcb(pcb);
		}	
		printf("Priority for %s is now %d\n",pcb->name,priority);
	}
	else { printf("ERROR: could not locate PCB named \"%s\"\n",name); }

	return 0;
}
int cmd_show_pcb(char *args) {
//	printf("Lists details for a single PCB\n");
//	printf("Usage: show name\n");
	pcb_rec *pcb;
	char *name;
	
	name = args;
	pcb = find_pcb(name);
	
	print_pcb_header();
	print_pcb(pcb);
	
	/*if (pcb != NULL) {
		printf("PCB Details for \"%s\"\n",pcb->name);
		printf("Class: %d\n",pcb->proc_class);
		printf("Priority: %d\n",pcb->priority);
		printf("State: %d\n",pcb->state);
	}
	else { printf("Could not locate PCB named \"%s\"\n",name); }
*/
	return 0;
}
int cmd_show_all(char *args) {

	cmd_show_ready(NULL);
	printf("\n");
	cmd_show_blocked(NULL);

	return 0;
}
int cmd_show_ready(char *args) {	
	if (get_ready_queue()->length != 0) {
		print_pcb_header();
		print_queue(PROC_STATE_READY);
		printf("+----------+-----+------+-------------+\n");
	}
	
	if (get_suspended_ready_queue()->length != 0) {
		if (get_ready_queue()->length == 0)
			print_pcb_header();
		print_queue(PROC_STATE_SUSPENDED_READY);
		printf("+----------+-----+------+-------------+\n");
	}
	
	if (get_ready_queue()->length == 0 && get_suspended_ready_queue()->length == 0) {
		printf("No ready processes\n");
	}
	
	return OK;
}
int cmd_show_blocked(char *args) {
	if (get_blocked_queue()->length != 0) {
		print_pcb_header();
		print_queue(PROC_STATE_BLOCKED);
		printf("+----------+-----+------+-------------+\n");
	}
	
	if (get_suspended_blocked_queue()->length != 0) {
		if (get_blocked_queue()->length == 0)
			print_pcb_header();
		print_queue(PROC_STATE_SUSPENDED_BLOCKED);
		printf("+----------+-----+------+-------------+\n");
	}
	
	if (get_blocked_queue()->length == 0 && get_suspended_blocked_queue()->length == 0) {
		printf("No blocked processes\n");
	}
	return OK;
}


/* ############################# MODULE R3 ############################# */

int cmd_dispatch(char *args) {
	//puts("About to dispatch");
	if (get_ready_queue()->length == 0) {
		printf("Ready queue empty, nothing to run.\nTry showall to list loaded processes\n");
		return ERROR;
	}
	dispatch();
	//puts("Finished dispatching");
	return OK;
}
int cmd_load_processes(char *args) {
	load_processes();
	puts("Test processes loaded");
	return OK;
}

/* ############################# MODULE R4 ############################# */

int cmd_load(char *args) {
	char *filename;
	int priority;
	
	filename = strtok(args," \t");
	priority = atoi(strtok(NULL," \t"));
	
	if (find_pcb(filename) != NULL) {
		printf("ERROR: process %s already exists.\n", filename);
		return ERROR;
	}
	
	printf("Attempting to load %s.mpx process with priority %d\n",filename,priority);
	load_process(filename, priority, PROC_CLASS_APP);
	return OK;
}

int cmd_kill(char *args) {
	pcb_rec *pcb;
	char *name;
	
	name = args;
	pcb = find_pcb(name);
		
	if (pcb == NULL) {
		printf("ERROR: could not locate PCB named \"%s\"\n",name);
		return ERROR;
	}
		
	if (pcb->proc_class == PROC_CLASS_SYS) {
		printf("ERROR: %s is a system process and cannot be killed.\n",pcb->name);
		return ERROR;
	}
	
	remove_pcb(pcb);
	free_pcb(pcb);
	printf("%s deleted\n",name);
	return OK;
}
